home *** CD-ROM | disk | FTP | other *** search
/ PsL Monthly 1993 December / PSL Monthly Shareware CD-ROM (December 1993).iso / prgmming / dos / c / cjdates.exe / DATES.ASM < prev    next >
Assembly Source File  |  1991-07-21  |  15KB  |  403 lines

  1.     %PAGESIZE 58,124
  2. ;*******************************************************************************
  3. ;
  4. ;                                 ZDAY and ZDATE
  5. ;
  6. ; These are routines for converting Gregorian dates to and from Day Numbers.
  7. ;
  8. ; The routines are called using the PASCAL calling sequence, and are FAR calls.
  9. ; All pointers are FAR pointers.
  10. ;
  11. ; The routines are written to run on an 8088 so as to be compatible with all
  12. ; machines based on this architecture, and use the Borland "PASCAL" calling
  13. ; sequence so as to be callable from programs compiled with both Borland's C
  14. ; and Pascal compilers.
  15. ;
  16. ; The Borland Turbo Assembler (V2.0 or better) is required to assemble this code.
  17. ;
  18. ;===============================================================================
  19. ;
  20. ; The C calling sequences are defined by these prototypes:
  21. ;
  22. ; unsigned long int far pascal ZDay( unsigned int Year, unsigned int Month,
  23. ;                                 unsigned int Day );
  24. ;
  25. ; int far pascal ZDate(unsigned long int DayNumber, unsigned int far *Year,
  26. ;                   unsigned int far *Month, unsigned int far *Day );
  27. ;
  28. ;-------------------------------------------------------------------------------
  29. ;
  30. ; The PASCAL prototypes (you will $L the object code from this assembly into
  31. ; a TPU) would be:
  32. ;
  33. ; function ZDay(Year, Month, Day : word) : longint;
  34. ;
  35. ; function ZDate(DayNumber : longint; var Year, Month, Day : word ) : boolean;
  36. ;
  37. ;-------------------------------------------------------------------------------
  38. ;
  39. ; ZDay returns a 32-bit unsigned integer representing the Day Number calculated
  40. ; from the supplied Gregorian date.  Although the Pascal call defines it as a
  41. ; longint, you will not have to worry about getting a negative number back.
  42. ;
  43. ; If the resulting Day Number is zero, you have supplied a Gregorian date that
  44. ; is too early or too far in the future for the calculations to be performed
  45. ; correctly with this routine's methods.
  46. ;
  47. ; The Year, Month and Day parameters are all unsigned 16-bit integers,    Year
  48. ; must be the FULL YEAR.  1902 must be supplied as 1902, not 02.  It must be
  49. ; in the range 1 to 25599 or zero will be returned.  Month and Day must be in
  50. ; the range 0-65535.  These restrictions should not be too restrictive for most
  51. ; purposes.
  52. ;
  53. ; Note that Day, Month and Year are all UNSIGNED.  When you are doing weird
  54. ; date calculations you must NEVER try to supply negative values for these.
  55. ; This is also true of the any Day Number you give to ZDate!  Negative numbers
  56. ; look like large positive numbers and will give Wrong Results!
  57. ;
  58. ; The same parameters are used with ZDate, except no value is returned since it
  59. ; is a procedure (void function) rather than a function.  Just supply the Day
  60. ; Number, and ZDate fills in the Year, Month and Day for you.  The supplied Day
  61. ; Number should be greater than 121 and less than 23920640.  For Day Numbers
  62. ; outside this range, ZDate returns a Gregorian date of 0-0-0 and a return
  63. ; value of 0 (FALSE in both Pascal and C).  A good conversion returns 1 (TRUE
  64. ; in both C and Pascal).
  65. ;
  66. ; NOTE:  ZDay can convert certain dates into Day Numbers that are outside the
  67. ; range that ZDate can convert back.  These dates, however, involve years
  68. ; that are either zero or very, very large, and are well outside the range of
  69. ; dates that we are likely to be interested in.
  70. ;
  71. ; A call to ZDay uses 14 bytes of space on the stack; a call to ZDate uses 26.
  72. ;
  73. ;===============================================================================
  74. ;
  75. ;                WHAT'S IT ALL FOR, ANYWAY?
  76. ;
  77. ; All dates, valid or not, convert to Day Numbers of some kind.  All Day
  78. ; Numbers convert to valid Gregorian dates.  So if you convert a Gregorian
  79. ; date to a Day Number and back, if the resulting Gregorian date doesn't match
  80. ; the original, the original was invalid.
  81. ;
  82. ; The Day Number is related to the Julian Day Number, but is not the same.
  83. ; It is valid only for the years since the Gregorian calendar was introduced.
  84. ;
  85. ; You can use the Day Numbers of two dates to find the number of days between
  86. ; them.
  87. ;
  88. ; The remainder resulting when the Day Number is divided by 7 is the day of the
  89. ; week, with 0 (Sunday) thru 6 (Saturday).
  90. ;
  91. ; To find the last day of a month, begin with the Gregorian date.  Add 1 to
  92. ; the month (even to December), set the Day to 0, convert to a Day Number, then
  93. ; back to Gregorian.
  94. ;
  95. ; To find the Julian day of the year (different from the Julian Day Number!),
  96. ; convert the given Gregorian date to a Day Number.  Then subtract from this
  97. ; the Day Number of January 0 (NOT 1!) of the same year.
  98. ;
  99. ; Convert a Julian Date to Gregorian by taking the Day Number of of January
  100. ; ZERO of the year.  Add the Julian day of the year to this, then convert
  101. ; to a Gregorian date.
  102. ;
  103. ;*******************************************************************************
  104. ;                           (c) Copyright 1991 Crazy Jack
  105. ;                                 All Rights Reserved
  106.     %NEWPAGE
  107.     IDEAL
  108.     MODEL    LARGE
  109.     CODESEG
  110. ;
  111. ; The simplest routine converts the Gregorian date to a Day Number:
  112. ;
  113.     PROC    PASCAL ZDAY FAR Year:WORD, Month:WORD, Day:WORD
  114.     PUBLIC    ZDAY
  115.     MOV    CX, [Year]        ;Get Year.
  116.     MOV    BX, [Month]        ;Get Month.
  117.     CMP    BX, 14            ;Month greater than 14 will give
  118.     JA    FIXMNTH         ;incorrect results.
  119. MNTHOK:
  120.     CMP    BL, 2            ;Is Month January or February?
  121.     JA    NOADJ            ;Jump if not,
  122.  
  123.     OR    CX, CX            ;else be sure year isn't zero (we're
  124.     JZ    DATEBAD         ;in trouble if we decrement zero),
  125.     DEC    CX            ;then shift calculations to joint
  126.     ADD    BX, 12            ;between February and March.
  127. NOADJ:
  128.     CMP    CH, 100         ;Any year too big to be divided by 100
  129.     JAE    DATEBAD         ;16-by-8-bit will cause divide overflow.
  130.     INC    BX            ;Need a little adjustment here---.
  131.  
  132.     MOV    AX, 43857        ;Calculate number of days due to months:
  133.     MUL    BX            ;First multiply by 306001, more than 16
  134.     SHL    BX, 1            ;bits worth.  The upper word is 4.  We
  135.     SHL    BX, 1            ;use shifts for speed.    Earlier test
  136.     ADD    DX, BX            ;against 2141 ensures no overflow and
  137.     MOV    BX, 10000        ;that we can divide by 10000.
  138.     DIV    BX            ;This gives INT(month * 30.6001).
  139.     MOV    BX, AX            ;Save the result (days due to months).
  140.  
  141.     MOV    AX, 365         ;Now for days due to years:
  142.     MUL    CX            ;Gives days in normal years.
  143.     ADD    AX, BX            ;Add in days due to months.
  144.     ADC    DX, 0            ;(There will be no carry from this!)
  145.     PUSH    AX            ;We need the AX for another divide.
  146.  
  147.     SHR    CX, 1            ;Find number of extra days due to
  148.     SHR    CX, 1            ;leap years (add a year for every 4).
  149.     MOV    AX, CX            ;Remove days for 400-year
  150.     MOV    BL, 25            ;Gregorian rule:
  151.     DIV    BL            ;First remove leap year day for each
  152.     XOR    AH, AH            ;century---
  153.     SUB    CX, AX            ;Result will always be positive.
  154.     SHR    AL, 1            ;Then add back a leap year day for
  155.     SHR    AL, 1            ;each 400 years.
  156.     ADD    AX, CX            ;No carry will occur.  Why?
  157.  
  158.     POP    BX
  159.     ADD    AX, BX            ;Add leap year days to days due to
  160.     ADC    DX, 0            ;month and year.
  161.     ADD    AX, [Day]        ;Fold in day of the month.
  162.     ADC    DX, 0
  163.     SUB    AX, 1            ;Finally, adjust so remainder from
  164.     SBB    DX, 0            ;divide by 7 gives day of week.
  165.                     ;Back to caller with Day Number
  166. GONE:                    ;Day Number in DX:AX.
  167.     RET
  168. ;
  169. FIXMNTH:                ;Sigh.    Month is too big to give valid
  170.     MOV    AX, BX            ;results, so we must reduce it.  We put
  171.     XOR    DX, DX            ;this here since it won't happen often
  172.     MOV    BX, 12            ;and we don't want to slow the main line.
  173.     DIV    BX            ;We convert it to years and months.
  174.     MOV    BX, DX            ;Remainder becomes new month.
  175.     ADD    CX, AX            ;Quotient is years.  Add to given year.
  176.     JNC    MNTHOK            ;Back to conversion if still in range.
  177. ;
  178. DATEBAD:
  179.     XOR    AX, AX            ;If something's wrong, we clear the
  180.     MOV    DX, AX            ;Day Number in DX:AX to zero
  181.     JMP    GONE            ;and clear out.
  182. ;
  183.     ENDP    ZDAY
  184.     %NEWPAGE
  185. ;
  186. ; Converting a Day Number back to a Gregorian date is more complicated:
  187. ;
  188.     PROC    PASCAL ZDATE FAR DayNumber:WORD:2, Year:FAR PTR WORD, Month:FAR PTR WORD, Day:FAR PTR WORD
  189.     PUBLIC    ZDATE
  190.     PUSH    SI
  191.     PUSH    DI
  192.     MOV    DX, [DayNumber+2]    ;Get Day Number from caller.
  193.     CMP    DX, 365         ;Bigger than this and we can't
  194.     JAE    RELAY2            ;extract the year!
  195.     MOV    AX, [DayNumber]     ;Okay, get the rest of the Day Number.
  196.  
  197.     MOV    SI, AX            ;We save a copy of it.
  198.     MOV    DI, DX
  199.     SUB    AX, 121         ;Fi